﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using SpatialHashLib.Topology;
using SpatialHashLib.Topology.Indexing;

namespace SpatialHashLib
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Auxilliary Classes, Structs, and Enums

        enum Operation
        {
            Add,
            Remove,
            Move,
            Resize,
            Search
        }

        #endregion

        Random rand;
        SpatialHash<Ellipse> spatialHash;
        Operation operation;

        Point mouseDown;
        Point itemPos;
        Ellipse changingItem;

        Dictionary<Ellipse, Brush> originalFills;

#if DEBUG
        Dictionary<CellEventArgs, Rectangle> displayGrid;
#endif

        public MainWindow()
        {
            InitializeComponent();

            rand = new Random();
            spatialHash = new SpatialHash<Ellipse>(20, 5);
            add.IsChecked = true;
            originalFills = new Dictionary<Ellipse, Brush>();

#if DEBUG
            displayGrid = new Dictionary<CellEventArgs, Rectangle>();

            spatialHash.CellAdded += new EventHandler<CellEventArgs>(spatialHash_CellAdded);
            spatialHash.CellRemoved += new EventHandler<CellEventArgs>(spatialHash_CellRemoved);
            spatialHash.CellSearch += new EventHandler<CellEventArgs>(spatialHash_CellSearch);
#endif


            //AddRandom(35);
        }

        Ellipse AddRandom(int size)
        {
            Ellipse item = new Ellipse()
            {
                Width = size,
                Height = size,
                Fill = new SolidColorBrush(
                    Color.FromArgb(
                        64,
                        (byte)rand.Next(0, 255),
                        (byte)rand.Next(0, 255),
                        (byte)rand.Next(0, 255)
                        )
                    ),
                Stroke = new SolidColorBrush(Colors.Black)
                {
                    Opacity = 0.4
                },
                StrokeThickness = 1
            };

            itemPos = new Point(rand.Next(100, 400), rand.Next(100, 400));
            //changingItem = item;

            Canvas.SetLeft(item, itemPos.X);
            Canvas.SetTop(item, itemPos.Y);

            mainCanvas.Children.Add(item);
            spatialHash.Add(item, item.GetExtent());
            return item;
        }

        void spatialHash_CellAdded(object sender, CellEventArgs e)
        {
            if (!displayGrid.ContainsKey(e))
            {
                Extent extent = e.Extent;

                Rectangle rect = new Rectangle()
                {
                    Width = extent.Width,
                    Height = extent.Height,
                    Stroke = new SolidColorBrush(Colors.Black)
                };

                Canvas.SetLeft(rect, extent.Min.X);
                Canvas.SetTop(rect, extent.Min.Y);

                gridlines.Children.Add(rect);
                displayGrid[e] = rect;
            }
        }

        void spatialHash_CellRemoved(object sender, CellEventArgs e)
        {
            if (displayGrid.ContainsKey(e))
            {
                gridlines.Children.Remove(displayGrid[e]);
                displayGrid.Remove(e);
            }
        }

        void spatialHash_CellSearch(object sender, CellEventArgs e)
        {
            if (displayGrid.ContainsKey(e))
            {
                //displayGrid[e].Fill = new SolidColorBrush(Colors.Yellow) { Opacity = .3 };
                //System.Diagnostics.Debug.WriteLine("CellSearched: " + e);
            }
        }

        private void mainCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            mouseDown = e.GetPosition(mainCanvas);

            switch (operation)
            {
                case Operation.Add:
                    {
                        #region Add

                        Ellipse item = new Ellipse()
                        {
                            Width = 8,
                            Height = 8,
                            Fill = new SolidColorBrush(
                                Color.FromArgb(
                                    64,
                                    (byte)rand.Next(0, 255),
                                    (byte)rand.Next(0, 255),
                                    (byte)rand.Next(0, 255)
                                    )
                                ),
                            Stroke = new SolidColorBrush(Colors.Black)
                            {
                                Opacity = 0.4
                            },
                            StrokeThickness = 1
                        };

                        itemPos = new Point(mouseDown.X - 4, mouseDown.Y - 4);
                        changingItem = item;

                        Canvas.SetLeft(item, itemPos.X);
                        Canvas.SetTop(item, itemPos.Y);

                        mainCanvas.Children.Add(item);
                        spatialHash.Add(item, item.GetExtent());

                        #endregion
                    }
                    break;

                case Operation.Remove:
                    {
                        #region Remove

                        Point delta;
                        Extent extent;

                        foreach (Ellipse item in spatialHash[new Coord(mouseDown.X, mouseDown.Y)])
                        {
                            extent = item.GetExtent();

                            delta = new Point(
                                mouseDown.X - extent.Center.X,
                                mouseDown.Y - extent.Center.Y
                                );

                            if (Math.Sqrt(delta.X * delta.X + delta.Y * delta.Y) <= extent.Width / 2)
                            {
                                spatialHash.Remove(item, extent);
                                mainCanvas.Children.Remove(item);
                                break;
                            }
                        }

                        #endregion
                    }
                    break;

                case Operation.Move:
                    {
                        #region Move

                        Point delta;
                        Extent extent;

                        foreach (Ellipse item in spatialHash[new Coord(mouseDown.X, mouseDown.Y)])
                        {
                            extent = item.GetExtent();

                            delta = new Point(
                                mouseDown.X - extent.Center.X,
                                mouseDown.Y - extent.Center.Y
                                );

                            if (Math.Sqrt(delta.X * delta.X + delta.Y * delta.Y) <= extent.Width / 2)
                            {
                                itemPos = new Point(extent.Min.X, extent.Min.Y);

                                changingItem = item;
                                break;
                            }
                        }

                        #endregion
                    }
                    break;

                case Operation.Resize:
                    {
                        #region Resize

                        Point delta;
                        Extent extent;

                        foreach (var item in spatialHash[new Coord(mouseDown.X, mouseDown.Y)])
                        {
                            extent = item.GetExtent();

                            delta = new Point(
                                mouseDown.X - extent.Center.X,
                                mouseDown.Y - extent.Center.Y
                                );

                            if (Math.Sqrt(delta.X * delta.X + delta.Y * delta.Y) <= extent.Width / 2)
                            {
                                itemPos = new Point(extent.Min.X, extent.Min.Y);

                                changingItem = item;
                                break;
                            }
                        }

                        #endregion
                    }
                    break;

                case Operation.Search:
                    {
                        #region Search

                        Canvas.SetLeft(searchRect, mouseDown.X);
                        Canvas.SetTop(searchRect, mouseDown.Y);
                        searchRect.Visibility = Visibility.Visible;
                        searchRect.Width = 10;
                        searchRect.Height = 10;

                        #endregion
                    }
                    break;
            }
        }

        private void mainCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            switch (operation)
            {
                case Operation.Add:
                case Operation.Resize:
                    if (changingItem != null)
                    {
                        Point p = e.GetPosition(mainCanvas);

                        p.X -= mouseDown.X;
                        p.Y -= mouseDown.Y;

                        double dist = Math.Sqrt(p.X * p.X + p.Y * p.Y);
                        if (dist > 4)
                        {
                            var oldExtent = changingItem.GetExtent();

                            Canvas.SetLeft(changingItem, oldExtent.Center.X - dist);
                            Canvas.SetTop(changingItem, oldExtent.Center.Y - dist);

                            changingItem.Width = dist * 2;
                            changingItem.Height = dist * 2;

                            var newExtent = changingItem.GetExtent();

                            spatialHash.Change(changingItem, oldExtent, newExtent);
                        }
                    }
                    break;

                case Operation.Move:
                    if (changingItem != null)
                    {
                        Point p = e.GetPosition(mainCanvas);

                        p.X -= mouseDown.X;
                        p.Y -= mouseDown.Y;

                        var oldExtent = changingItem.GetExtent();

                        Canvas.SetLeft(changingItem, itemPos.X + p.X);
                        Canvas.SetTop(changingItem, itemPos.Y + p.Y);

                        var newExtent = changingItem.GetExtent();

                        spatialHash.Change(changingItem, oldExtent, newExtent);
                    }
                    break;

                case Operation.Search:
                    {
                        Point p = e.GetPosition(mainCanvas);

                        if (p.X < mouseDown.X)
                        {
                            Canvas.SetLeft(searchRect, p.X);
                            searchRect.Width = mouseDown.X - p.X;
                        }
                        else
                            searchRect.Width = p.X - mouseDown.X;

                        if (p.Y < mouseDown.Y)
                        {
                            Canvas.SetTop(searchRect, p.Y);
                            searchRect.Height = mouseDown.Y - p.Y;
                        }
                        else
                            searchRect.Height = p.Y - mouseDown.Y;
                    }
                    break;
            }
        }

        private void mainCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (changingItem != null)
            {
                changingItem = null;
            }

            if (searchRect.Visibility == Visibility.Visible)
            {
                searchRect.Visibility = Visibility.Collapsed;
            }
        }

        private void add_Checked(object sender, RoutedEventArgs e)
        {
            operation = Operation.Add;
        }

        private void remove_Checked(object sender, RoutedEventArgs e)
        {
            operation = Operation.Remove;
        }

        private void move_Checked(object sender, RoutedEventArgs e)
        {
            operation = Operation.Move;
        }

        private void resize_Checked(object sender, RoutedEventArgs e)
        {
            operation = Operation.Resize;
        }

        private void search_Checked(object sender, RoutedEventArgs e)
        {
            operation = Operation.Search;
        }
    }
}
